package com.kelai.resource.impl;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.kelai.common.constants.DeviceStatusEnum;
import com.kelai.domain.*;
import com.kelai.dto.DeviceExceptionDto;
import com.kelai.dto.DeviceStatisticsReportDto;
import com.kelai.dto.ShopDataExcelDto;
import com.kelai.resource.IReportResource;
import com.kelai.resource.JsonCommonResource;
import com.kelai.response.JsonResponse;
import com.kelai.service.ICommonService;
import com.kelai.service.ICustomerService;
import com.kelai.service.IDeviceService;
import com.kelai.service.IReportService;
import com.kelai.service.impl.DefaultCommonService;
import com.kelai.utils.BreezeeUtils;
import com.kelai.utils.ExcelUtils;
import javafx.util.Callback;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 报表服务实现
 * Created by Silence on 2016/11/19.
 */
@SuppressWarnings("unchecked")
@RestController
@RequestMapping("/report")
public class DefaultReportResource extends JsonCommonResource implements IReportResource {

    private final static Logger logger = LoggerFactory.getLogger(DefaultReportResource.class);

    private ICommonService vendorService;

    private ICommonService deviceStateDateService;

    @Resource
    private IReportService reportService;
    @Resource
    private ICustomerService customerService;
    @Resource
    private IDeviceService deviceService;

    /**
     * 索尼中国门店周报导出
     * @param
     * @return
     */
    @RequestMapping(value = "/shop_download", method = RequestMethod.POST ,
            produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public ResponseEntity<InputStreamResource> export(String _shopType,
                                                      String _startDate,String _endDate) throws IOException {

        logger.info("~~~~索尼周报表导出进行中...~~~~");
        //查找所有索尼中国下的门店
        List<Object[]> shopList = customerService.findShopsByCusId("9236b103947f4f71b261bcf958286b2c");
        logger.info("~~~~索尼中国下总共"+shopList.size()+"家门店~~~~");
        //按门店类型和开关店时间过滤数据
        Date currentDate = new Date();
        if(_shopType != null && !Objects.equals("",_shopType.trim())){
            shopList = shopList.stream().filter( shop -> {
                Boolean flag = true;
                //门店类型
                if(shop[3] != null && !Objects.equals(_shopType,shop[3].toString())){
                    flag = false;
                }else if(shop[3] == null){
                    flag = false;
                }
                //开业时间
                if(shop[9] != null){
                    if(currentDate.compareTo((Date)shop[9]) < 0){
                        flag = false;
                    }
                }
                //停业时间
                if(shop[10] != null){
                    if(currentDate.compareTo((Date)shop[10]) > 0){
                        flag = false;
                    }
                }
                return flag;
            }).collect(Collectors.toList());
        }
        List<String> shopIdList = shopList.stream().map( shop -> {
            return shop[0].toString();
        }).collect(Collectors.toList());
        logger.info("~~~~索尼中国过滤后剩下"+shopIdList.size()+"家门店~~~~");

        ShopDataDayEntity condition = new ShopDataDayEntity();
        condition.getProperties().put("_shopIds", shopIdList);
        if(_shopType != null && !"".equals(_shopType.trim())){
            condition.getProperties().put("_shopType", _shopType);
        }
        condition.getProperties().put("_startDate", _startDate);
        condition.getProperties().put("_endDate", _endDate);
        JsonResponse rep = _pageAllByBi("SHOP_DAY", condition.getProperties());
        List<ShopDataDayEntity> shopDataList = (List<ShopDataDayEntity>)rep.getRows();
        logger.info("~~~~索尼中国在"+_startDate+"到"+_endDate+"时间范围内总共有"+shopDataList.size()+"条BI日统计数据~~~~");
        //统计每个门店的客流量
        Map<String , Integer> shopCustFlowMap = shopDataList.stream()
                .collect(Collectors.groupingBy(ShopDataDayEntity::getShopId,
                        Collectors.summingInt(ShopDataDayEntity::getDxin)));

        //按统计时间查出原始数据
        DeviceStateDataEntity deviceStatusCondition = new DeviceStateDataEntity();
        deviceStatusCondition.getProperties().put("happenTime_gt",_startDate.replaceAll("-","").trim()+"000000");
        deviceStatusCondition.getProperties().put("happenTime_le",_endDate.replaceAll("-","").trim()+"235959");
        List<DeviceStateDataEntity> deviceStateList =
                (List<DeviceStateDataEntity>)_pageAll(deviceStateDateService,deviceStatusCondition).getRows();
        logger.info("~~~~索尼中国原始设备流量数据总共"+deviceStateList.size()+"~~~~");
        //按设备分组
        Map<String,List<DeviceStateDataEntity>> deviceStateMap = deviceStateList.stream()
                .collect(Collectors.groupingBy( deviceState -> {
                    return deviceState.getDevice().getId();
                }));

        //统计异常率
        Map<String , String> errorMap = Maps.newConcurrentMap();
        deviceStateMap.forEach((deviceId , dataCollect) -> {
            final Integer errorCnt[] = new Integer[1]; // 异常数
            errorCnt[0] = 0;

            DeviceStatisticsReportDto dto = dateRangeSplit(deviceId,_startDate,_endDate);
            dto.getItemList().forEach( item -> {
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                LocalDateTime localDateTimeBegin = item.get("begin");
                LocalDateTime localDateTimeEnd = item.get("end");
                //两小时内的流水
                List<DeviceStateDataEntity> dataList = dataCollect.stream().filter(data -> {
                    LocalDateTime now = LocalDateTime.parse(data.getHappenTime(),dtf);
                    if(now.isAfter(localDateTimeBegin) && now.isBefore(localDateTimeEnd)){
                        return true;
                    }else{
                        return false;
                    }
                }).collect(Collectors.toList());

                if(CollectionUtils.isNotEmpty(dataList)){
                    //两小时内进出店总人数
                    int twoHourTotal = dataList.stream().map(data -> {
                        return (data.getDxin() == null ? 0 :  data.getDxin())
                                + (data.getDxout() == null ? 0 : data.getDxout());
                    }).reduce(0, (sum , num ) -> {
                        return sum + num;
                    });
                    logger.info("~~~~~两小时内进出店总数:"+twoHourTotal+"~~~~~");
                    //如果没有客流数再检查其它异常,只要有客流数就不算异常
                    if(twoHourTotal < 1){
                        //按时间倒序
                        List<DeviceStateDataEntity> list =  dataList.stream()
                                .sorted((dd2,dd1) -> dd1.getHappenTime().compareTo(dd2.getHappenTime()))
                                .collect(Collectors.toList());
                        if(CollectionUtils.isNotEmpty(list)){
                            //取两小时的最后一条记录
                            DeviceStateDataEntity lastRecord = list.get(0);
                            //失焦
                            Integer focus = lastRecord.getFocus() == null ? 0 : lastRecord.getFocus();
                            if(Objects.equals(1,focus)){
                                Long normalTotal = dataList.stream().filter(data -> {
                                    if(Objects.equals(1,data.getFocus())){
                                        return false;
                                    }else{
                                        return true;
                                    }
                                }).count();
                                if (normalTotal > 0){
                                    focus = 0;
                                }
                            }
                            //电量
                            Integer battery = lastRecord.getBattery() == null ? 15 : lastRecord.getBattery();
                            //异常
                            if( Objects.equals(focus,1) || battery < 10){
                                errorCnt[0] = errorCnt[0]+1;
                            }
                        }
                    }
                }else{//两小时内一条记录都没有，肯定有问题
                    errorCnt[0] = errorCnt[0]+1;
                }
            });

            logger.info("~~~~~~~异常数:"+errorCnt[0]+"~~~~~~~");
            String errorRateStr = new BigDecimal(errorCnt[0])
                    .divide(new BigDecimal(dto.getTotal()),2,BigDecimal.ROUND_HALF_UP)
                    .multiply(new BigDecimal(100))
                    .toString();
            errorMap.put(deviceId , errorRateStr);

        });

        //最终导出结果
        List<ShopDataExcelDto> excelData = Lists.newArrayList();
        //0pk_id,1code,2name,3shop_type,4contact_person,5telephone,6mobile,7address
        for(Object[] item : shopList){

            ShopDataExcelDto dto = new ShopDataExcelDto();
            if(item[3]!=null){
                dto.setShopType(item[3].toString());
            }else{
                dto.setShopType("");
            }
            if(item[1]!=null){
                dto.setShopCode(item[1].toString());
            }else{
                dto.setShopCode("");
            }
            if(item[2]!=null){
                dto.setShopName(item[2].toString());
            }else{
                dto.setShopName("");
            }
            if(item[7]!=null){
                dto.setShopAddress(item[7].toString());
            }else{
                dto.setShopAddress("");
            }
            if(item.length>=9){
                if(item[8]!=null){
                    dto.setGuaranteeDeadLine(item[8].toString().substring(0,10));
                }else{
                    dto.setGuaranteeDeadLine(null);
                }
            }
            if(item[4]!=null){
                dto.setContactName(item[4].toString());
            }else{
                dto.setContactName("");
            }

            dto.setContactPhone(item[5] != null ? item[5].toString()
                    : (item[6] != null ? item[6].toString(): ""));

            if(item[4]!=null){
                dto.setContactName(item[4].toString());
            }else{
                dto.setContactName("");
            }
            String deviceCodes = "";
            String deviceStatus = "";
            String deviceExceptionRates = "";
            String deviceMemos = "";

            CustomerEntity customer = customerService.findById(item[0].toString());
            Set<DeviceEntity> deviceEntities = customer.getDevices();
            for(DeviceEntity device : deviceEntities){
                deviceCodes += "\r\n" + device.getCode();
                deviceStatus += "\r\n" + DeviceStatusEnum.getTextByVal(device.getStatus());
                if(device.getMemo()!=null){
                    deviceMemos +=" " +device.getMemo();
                }

                String exceptionRate = "";
                if(device.getCode().length() == 4){
                    exceptionRate = "老设备无法统计";
                }else{
                    exceptionRate = errorMap.get(device.getId()) == null ? "100%" : errorMap.get(device.getId())+"%";
                }

                deviceExceptionRates += "\r\n"+exceptionRate;
            }
            dto.setDeviceCode(deviceCodes);
            dto.setDeviceStatus(deviceStatus);
            dto.setExceptionRate(deviceExceptionRates);
            dto.setMemo(deviceMemos);
            dto.setCustomerFlow(shopCustFlowMap.get(item[0].toString()) != null ? shopCustFlowMap.get(item[0].toString()).toString(): "0" );
            excelData.add(dto);
        }

        File templateFile = null;
        templateFile = ExcelUtils.export(ShopDataExcelDto.class, "门店报表", "门店报表（"+_startDate+"~"+_endDate+"）", excelData);

        logger.info("~~~~索尼周报表导出结束~~~~");
        FileSystemResource file = new FileSystemResource(templateFile);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Content-Disposition", "attachment; filename="+new String((file.getFilename()).getBytes("gb2312"), "iso-8859-1"));
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.contentLength())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(new InputStreamResource(file.getInputStream()));
    }



    /**
     * 索尼中国设备周报表导出
     * @param
     * @return
     */
    @RequestMapping(value = "/device_exception_download", method = RequestMethod.POST , produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    ResponseEntity<InputStreamResource> exportDeviceException(String _shopType,String _startDate,String _endDate)throws IOException{


        String fileName = "异常设备统计（"+_startDate+"~"+_endDate+"）";

        Long s1 = System.currentTimeMillis();

        logger.info("~~enter device data export~~");
        ShopDataDayEntity sdde = new ShopDataDayEntity();

        List<Object[]> shopList = customerService.findShopsByCusId("9236b103947f4f71b261bcf958286b2c");

        if(_shopType!=null&&!"".equals(_shopType.trim())){
            Iterator<Object[]> iterator = shopList.iterator();
            while(iterator.hasNext()){
                Object[] shop = iterator.next();
                if(!shop[3].toString().equals(_shopType)){
                    iterator.remove();
                }
            }
        }

        //获得这个时间段的所有sony上传设备的状态数据
        String startDate = _startDate+" 00:00:00";
        String endDate = _endDate+" 00:00:00";
        startDate = dateStrParse(startDate);
        endDate = dateStrParse(endDate);
        Long lowDate = Long.parseLong(startDate.substring(0,8)+"000000")-1;
        Long highDate = Long.parseLong(endDate.substring(0,8)+"000000")+1000000;
        DeviceStateDataEntity deviceStateDataEntity = new DeviceStateDataEntity();
        deviceStateDataEntity.getProperties().put("happenTime_gt",lowDate);
        deviceStateDataEntity.getProperties().put("happenTime_le",highDate);
        List<DeviceStateDataEntity> deviceStateDataEntities = (List<DeviceStateDataEntity>)_pageAll(deviceStateDateService,deviceStateDataEntity).getRows();

        Map<String,List<DeviceStateDataEntity>> deviceStateGroup = new HashMap<String,List<DeviceStateDataEntity>>();
        for (DeviceStateDataEntity temp:deviceStateDataEntities) {
            if(deviceStateGroup.get(temp.getDevice().getId())!=null){
                deviceStateGroup.get(temp.getDevice().getId()).add(temp);
            }else{
                List<DeviceStateDataEntity> list = new ArrayList<DeviceStateDataEntity>();
                list.add(temp);
                deviceStateGroup.put(temp.getDevice().getId(),list);
            }
        }

        //0pk_id,1code,2name,3shop_type,4contact_person,5telephone,6mobile,7address
        logger.info("~~shopList size:"+shopList.size()+"~~");

        List<DeviceExceptionDto> deviceExceptionDtos = new ArrayList<DeviceExceptionDto>();
        for(Object[] item : shopList){
            CustomerEntity customer = customerService.findById(item[0].toString());
            Set<DeviceEntity> deviceEntities = customer.getDevices();

            for(DeviceEntity device : deviceEntities){
                DeviceExceptionDto dto = new DeviceExceptionDto();
                dto.setShopType(item[3].toString());
                dto.setShopCode(item[1].toString());

                dto.setShopName(item[2].toString());
                dto.setShopAddress(item[7].toString());
                dto.setContactName(item[4].toString());
                dto.setContactPhone(item[5] != null ? item[5].toString()
                        : (item[6] != null ? item[6].toString(): ""));

                dto.setContactName(item[4].toString());
                dto.setDeviceCode(device.getCode());

                /*
                    计算设备的异常率
                 */
                List<DeviceStateDataEntity> deviceStateData =deviceStateGroup.get(device.getId());
                double offLineRate = getOffLineRate(deviceStateData,device,getBetweenDayNum(_startDate,_endDate));
                String focusAndLowVoltageRate =getFocusAndLowVoltageRate(deviceStateData,device.getId(),lowDate,highDate);

                double focusRate =  0;
                double lowVoltageRate =  0;
                if(focusAndLowVoltageRate!=null){
                    String[] rates =  focusAndLowVoltageRate.split("-");
                    focusRate = Double.parseDouble(rates[0]);
                    lowVoltageRate = Double.parseDouble(rates[1]);
                }

                double exceptionRate = (offLineRate+focusRate+lowVoltageRate)/3;

                //比率保留4位小数 四舍五入
                DecimalFormat df = new DecimalFormat("0.0000");
                String exceptionRateStr = df.format(exceptionRate);
                exceptionRateStr = exceptionRateStr.substring(2,4)+"."+exceptionRateStr.substring(4)+"%";

                dto.setExceptionRate(exceptionRateStr);
                dto.setMemo(device.getMemo());

                deviceExceptionDtos.add(dto);
            }
        }
        File templateFile = null;
        templateFile = ExcelUtils.export(DeviceExceptionDto.class, "异常设备统计", fileName, deviceExceptionDtos);

        FileSystemResource file = new FileSystemResource(templateFile);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Content-Disposition", "attachment; filename="+new String((file.getFilename()).getBytes("gb2312"), "iso-8859-1"));
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.contentLength())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(new InputStreamResource(file.getInputStream()));
    }


    @RequestMapping(value = "/", method = RequestMethod.POST)
    @Override
    public JsonResponse<ReportEntity> page(@RequestBody ReportEntity reportEntity) {

        return _pageAll(reportService, reportEntity);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @Override
    public JsonResponse<ReportEntity> findById(@PathVariable String id) {
        JsonResponse<ReportEntity> result = _findOne(reportService, id, 0);
        return result;
    }

    @RequestMapping(value = "/code/{code}", method = RequestMethod.GET)
    @Override
    public JsonResponse<ReportEntity> findByCode(@PathVariable String code) {
        return _findOne(reportService, code, 1);
    }

    @RequestMapping(value = "/", method = RequestMethod.PUT)
    @Override
    public JsonResponse<ReportEntity> save(@RequestBody ReportEntity reportEntity) {
        return _saveInfo(reportService, reportEntity, new Callback<ReportEntity, Object>() {
            @Override
            public Object call(ReportEntity param) {
                param.setCode(param.getId());
                return null;
            }
        });
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    @Override
    public JsonResponse<ReportEntity> delete(@PathVariable String id) {
        return _delete(reportService, id);
    }

    @Override
    public void initService() {
        if (vendorService == null)
            vendorService = new DefaultCommonService("vendorRepository");
        if(deviceStateDateService==null)
            deviceStateDateService = new DefaultCommonService("deviceStateDataRepository");
    }


    /**
     * yyyy-MM-dd HH:mm:ss   ->   yyyyMMddHHmmss
     * @param dateStr
     * @return
     */
    private String dateStrParse(String dateStr){

        String[] s = dateStr.split(" ");
        String[] ymd = s[0].split("-");
        String[] hms = s[1].split(":");

        return ymd[0]+ymd[1]+ymd[2]+hms[0]+hms[1]+hms[2];
    }


    /**
     * 获取某段时间 某设备的离线率
     * @param deviceStateDataEntities
     * @param device
     * @param dayNum
     * @return
     */
    private double getOffLineRate(List<DeviceStateDataEntity> deviceStateDataEntities,DeviceEntity device,int dayNum){

        //获得某时间段内同一设备的设备共接收多少条状态数据
        int receiveNum = 0;
        if(deviceStateDataEntities!=null){
            receiveNum = deviceStateDataEntities.size();
        }

        //获得某时间段内同一设备的设备应接收多少条状态数据
        int totalNum = 0;
        if(device.getUploadCycle()!=null&&device.getUploadCycle()!=0){
            Integer hours = Integer.parseInt(device.getCustomer().getCloseTime().split(":")[0])-Integer.parseInt(device.getCustomer().getOpenTime().split(":")[0]);
            totalNum = dayNum*hours*(60/device.getRecordCycle());
        }
        if(receiveNum>=totalNum){
            return 0;
        }else{
            return 1-((receiveNum*1.0)/totalNum);
        }
    }

    /**
     * 获取某段时间 某设备的失焦率,低电量率
     * @param deviceId
     * @return
     */
    private String getFocusAndLowVoltageRate(List<DeviceStateDataEntity> deviceStateDataEntities,String deviceId,Long lowDate,Long highDate){



        if(deviceStateDataEntities!=null&&deviceStateDataEntities.size()>0){
            int focusCount = 0;//失焦数量
            int lowVoltageCount = 0;
            for (DeviceStateDataEntity dsde:deviceStateDataEntities) {
                if(dsde.getFocus()==1){
                    focusCount++;
                }
                if(dsde.getBattery()<10){
                    lowVoltageCount++;
                }
            }
            double focusRate = (focusCount*1.0)/deviceStateDataEntities.size();
            double lowVoltageRate = (lowVoltageCount*1.0)/deviceStateDataEntities.size();
            //比率保留4位小数 四舍五入
            DecimalFormat df = new DecimalFormat("0.0000");
            return df.format(focusRate)+"-"+df.format(lowVoltageRate);
        }else{
            return null;
        }
    }


    /**
     * 获得持续天数
     * @param startDate  yyyy-MM-dd
     * @param endDate
     * @return
     */
    private int getBetweenDayNum(String startDate,String endDate){

        try {
            Date start = BreezeeUtils.DATE_FORMAT_SHORT.parse(startDate);
            Date end = BreezeeUtils.DATE_FORMAT_SHORT.parse(endDate);
            return  (int)(end.getTime()-start.getTime())/(24*60*60*1000)+1;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * 将统计时段按2小时1片划分，并且是在营业时间范围内
     * @param deviceId
     * @param startDateStr
     * @param endDateStr
     * @return
     */
    private DeviceStatisticsReportDto dateRangeSplit(String deviceId , String startDateStr, String endDateStr){

        DeviceStatisticsReportDto result = new DeviceStatisticsReportDto();

        DeviceEntity deviceEntity = deviceService.findById(deviceId);
        CustomerEntity shopEntity = deviceEntity.getCustomer();
        String openTimeStr = StringUtils.isNotEmpty(shopEntity.getOpenTime()) ?
                (shopEntity.getOpenTime().length() == 4 ? "0"+shopEntity.getOpenTime()+":00" : shopEntity.getOpenTime()+":00")
                : "00:00:00";
        String closeTimeStr = StringUtils.isNotEmpty(shopEntity.getCloseTime()) ?
                (shopEntity.getCloseTime().length() == 4 ? "0"+shopEntity.getCloseTime()+":59": shopEntity.getCloseTime()+":59")
                : "23:59:59";

        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse(startDateStr,dateFormatter);
        LocalDate endDate = LocalDate.parse(endDateStr,dateFormatter);
        Long diffDays = endDate.toEpochDay() - startDate.toEpochDay() + 1;
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        LocalTime openTime = LocalTime.parse(openTimeStr,timeFormatter);
        LocalTime closeTime = LocalTime.parse(closeTimeStr,timeFormatter);
        Long diffHours = ((new BigDecimal(closeTime.getHour() - openTime.getHour())
                .divide(new BigDecimal(2)).setScale(0, BigDecimal.ROUND_CEILING))).longValue();

        result.setTotal(diffDays * diffHours);
        logger.info("~~~~总共"+result.getTotal()+"时间片~~~~");

        List<Map<String,LocalDateTime>> list = Lists.newArrayList();
        for(int i = 0 ; i < diffDays ; i++){
            LocalDate date = startDate.plusDays(i);
            LocalTime time = openTime;
            for(int j = 0 ; j < diffHours ; j++){
                LocalDateTime begin = LocalDateTime.of(date,time);
                LocalDateTime end = LocalDateTime.of(date,time.plusHours(2));
                time = time.plusHours(2);
                Map<String,LocalDateTime> map = Maps.newConcurrentMap();
                map.put("begin",begin);
                map.put("end",end);
                logger.info("~~~~时间范围:"+begin+"至"+end+"时间片~~~~");
                list.add(map);
            }
        }
        result.setItemList(list);
        return result;
    }


}
